#ifndef __SCOPE_H
#define __SCOPE_H 1

#include <type.h>
#include <unordered_map>
#include <list>

#include <common.h>

class Scope {

    class scope {
    public:
        std::unordered_map<std::string, std::pair<size_t, int>> vars;
        std::unordered_map<std::string, size_t> size;
        std::unordered_map<std::string, size_t> type;
    }; 

    std::list<scope> sp;
    size_t static_length;
    size_t padding, length;
    size_t function_frame, param_frame;

public:
    Scope() {
        static_length = 0;
        sp.push_back(scope());
    }

    void init(size_t pad, size_t frame) {
        padding = pad; function_frame = frame;
        param_frame = frame;
        length = 0;
        sp.push_front(scope());
    }
    void deinit() { sp.pop_front(); }

    void Enter() { sp.push_front(scope()); }

    void Leave() { sp.pop_front(); }

    std::pair<size_t, int> find(std::string name) {
        for (auto& m : sp) 
            if (m.vars.count(name)) {
                if (m.vars[name].second == gp) return m.vars[name];
                return {m.vars[name].first+padding, m.vars[name].second};
            }
        return {0, 0};
    }
    size_t size(std::string name) {
        for (auto& m : sp) if (m.size.count(name)) return m.size[name];
        return 0;
    }
    Type type(std::string name) {
        for (auto& m : sp) if (m.type.count(name)) return m.type[name];
        return Type{};
    }

    void push(std::string name, Type type, bool is_static) {
        auto& m  = sp.front();
        if (is_static)
            m.vars[name] = {Align(static_length, type), gp};
        else 
            m.vars[name] = {Align(length, type), fp};
        m.size[name] = sizeType(type);
    }
    void push_param(std::string name, Type type) {
        auto& m = sp.front();
        m.vars[name] = {Align(param_frame, type) - padding, fp};
        m.size[name] = sizeType(type);
    }

    size_t frame_size() const { return function_frame; }
};


#endif //__SCOPE_H